#import "CheckPermissions.h"
#import <AVFoundation/AVFoundation.h>
#import <Photos/Photos.h>
#import <PhotosUI/PhotosUI.h>
#import <MediaPlayer/MediaPlayer.h>
#import <CoreLocation/CoreLocation.h>
#import <React/RCTUtils.h>
#import <CoreBluetooth/CoreBluetooth.h>
#import <EventKit/EventKit.h>



typedef enum {
    RNPermissionStatusNotAvailable = 0,
    RNPermissionStatusNotDetermined = 1,
    RNPermissionStatusRestricted = 2,
    RNPermissionStatusDenied = 3,
    RNPermissionStatusAuthorized = 4,
    RNPermissionStatusLimited = 5,
} RNPermissionStatus;


@interface CheckPermissions ()<CLLocationManagerDelegate, CBPeripheralManagerDelegate>

@property (nonatomic, strong) CLLocationManager *locationManager;
@property (nonatomic, strong) CBPeripheralManager* manager;

@property (nonatomic, strong) void (^resolve)(id result);
@property (nonatomic, strong) void (^reject)(NSError *error);

@end

@implementation CheckPermissions



/**
 * 麦克风权限获取
 * Microphone
 */

- (void)checkMicrophoneWithResolver:(void (^ _Nonnull)(id))resolve
                           rejecter:(void (__unused ^ _Nonnull)(NSError * _Nonnull))reject {
    switch ([[AVAudioSession sharedInstance] recordPermission]) {
        case AVAudioSessionRecordPermissionUndetermined:
            return resolve(@{@"status":@(RNPermissionStatusNotDetermined)});
        case AVAudioSessionRecordPermissionDenied:
            return resolve(@{@"status":@(RNPermissionStatusDenied)});
        case AVAudioSessionRecordPermissionGranted:
            return resolve(@{@"status":@(RNPermissionStatusAuthorized)});
    }
}

- (void)requestMicrophoneWithResolver:(void (^ _Nonnull)(id))resolve
                             rejecter:(void (^ _Nonnull)(NSError * _Nonnull))reject {
    [[AVAudioSession sharedInstance] requestRecordPermission:^(__unused BOOL granted) {
        [self checkMicrophoneWithResolver:resolve rejecter:reject];
    }];
}

/**
 * Calendar
 */
- (void)checkCalendarWithResolver:(void (^ _Nonnull)(id))resolve
                         rejecter:(void (__unused ^ _Nonnull)(NSError * _Nonnull))reject {
    switch ([EKEventStore authorizationStatusForEntityType:EKEntityTypeEvent]) {
        case EKAuthorizationStatusNotDetermined:
            return resolve(@{@"status":@(RNPermissionStatusNotDetermined)});
        case EKAuthorizationStatusRestricted:
            return resolve(@{@"status":@(RNPermissionStatusRestricted)});
        case EKAuthorizationStatusDenied:
            return resolve(@{@"status":@(RNPermissionStatusDenied)});
        case EKAuthorizationStatusAuthorized:
            return resolve(@{@"status":@(RNPermissionStatusAuthorized)});
    }
}

- (void)requestCalendarWithResolver:(void (^ _Nonnull)(id))resolve
                           rejecter:(void (^ _Nonnull)(NSError * _Nonnull))reject {
    [[EKEventStore new] requestAccessToEntityType:EKEntityTypeEvent
                                       completion:^(__unused BOOL granted, NSError * _Nullable error) {
        if (error != nil) {
            reject(error);
        } else {
            [self checkCalendarWithResolver:resolve rejecter:reject];
        }
    }];
}

/**
 * Camera
 * 相机权限获取
 */
- (void)checkCameraWithResolver:(void (^ _Nonnull)(id))resolve
                       rejecter:(void (__unused ^ _Nonnull)(NSError * _Nonnull))reject {
    switch ([AVCaptureDevice authorizationStatusForMediaType:AVMediaTypeVideo]) {
        case AVAuthorizationStatusNotDetermined:
            return resolve(@{@"status":@(RNPermissionStatusNotDetermined)});
        case AVAuthorizationStatusRestricted:
            return resolve(@{@"status":@(RNPermissionStatusRestricted)});
        case AVAuthorizationStatusDenied:
            return resolve(@{@"status":@(RNPermissionStatusDenied)});
        case AVAuthorizationStatusAuthorized:
            return resolve(@{@"status":@(RNPermissionStatusAuthorized)});
    }
}

- (void)requestCameraWithResolver:(void (^ _Nonnull)(id))resolve
                         rejecter:(void (^ _Nonnull)(NSError * _Nonnull))reject {
    [AVCaptureDevice requestAccessForMediaType:AVMediaTypeVideo
                             completionHandler:^(__unused BOOL granted) {
        [self checkCameraWithResolver:resolve rejecter:reject];
    }];
}

/**
 * PhotoLibrary
 * 相册
 *
 */
- (void)checkPhotoLibraryWithResolver:(void (^ _Nonnull)(id))resolve
                             rejecter:(void (__unused ^ _Nonnull)(NSError * _Nonnull))reject {
    PHAuthorizationStatus status;

    if (@available(iOS 14.0, *)) {
        status = [PHPhotoLibrary authorizationStatusForAccessLevel:PHAccessLevelReadWrite];
    } else {
        status = [PHPhotoLibrary authorizationStatus];
    }
    RNPermissionStatus _status;
    switch (status) {
        case PHAuthorizationStatusNotDetermined:
            _status = RNPermissionStatusNotDetermined;
            break;
        case PHAuthorizationStatusRestricted:
            _status = RNPermissionStatusRestricted;
            break;
        case PHAuthorizationStatusDenied:
            _status = RNPermissionStatusDenied;
            break;
        case PHAuthorizationStatusLimited:
            _status = RNPermissionStatusLimited;
            break;
        case PHAuthorizationStatusAuthorized:
            _status = RNPermissionStatusAuthorized;
            break;
        default:
            _status = RNPermissionStatusNotDetermined;
            break;
    }
    return resolve(@{@"status": @(_status)});
}

- (void)requestPhotoLibraryWithResolver:(void (^ _Nonnull)(id))resolve
                               rejecter:(void (^ _Nonnull)(NSError * _Nonnull))reject {
    if (@available(iOS 14.0, *)) {
        [PHPhotoLibrary requestAuthorizationForAccessLevel:PHAccessLevelReadWrite handler:^(__unused PHAuthorizationStatus status) {
            [self checkPhotoLibraryWithResolver:resolve rejecter:reject];
        }];
    } else {
        [PHPhotoLibrary requestAuthorization:^(__unused PHAuthorizationStatus status) {
            [self checkPhotoLibraryWithResolver:resolve rejecter:reject];
        }];
    }
}

- (void)openLimitedPhotoLibraryPickerWithResolver:(RCTPromiseResolveBlock _Nonnull)resolve
                                         rejecter:(RCTPromiseRejectBlock _Nonnull)reject {
    if (@available(iOS 14, *)) {
        if ([PHPhotoLibrary authorizationStatusForAccessLevel:PHAccessLevelReadWrite] != PHAuthorizationStatusLimited) {
            return reject(@"cannot_open_limited_picker", @"Photo library permission isn't limited", nil);
        }

        UIViewController *presentedViewController = RCTPresentedViewController();
        [[PHPhotoLibrary sharedPhotoLibrary] presentLimitedLibraryPickerFromViewController:presentedViewController];

        resolve(@(true));
    } else {
        reject(@"cannot_open_limited_picker", @"Only available on iOS 14 or higher", nil);
    }
}
- (void)checkPhotoLibraryAddOnlyWithResolver:(void (^ _Nonnull)(id))resolve
                                    rejecter:(void (__unused ^ _Nonnull)(NSError * _Nonnull))reject {
    RNPermissionStatus _status;
    if (@available(iOS 14.0, *)) {
        switch ([PHPhotoLibrary authorizationStatusForAccessLevel:PHAccessLevelAddOnly]) {
            case PHAuthorizationStatusNotDetermined:
                _status = RNPermissionStatusNotDetermined;
                break;
            case PHAuthorizationStatusRestricted:
                _status = RNPermissionStatusRestricted;
                break;
            case PHAuthorizationStatusDenied:
                _status = RNPermissionStatusDenied;
                break;
            case PHAuthorizationStatusLimited:
                _status=RNPermissionStatusLimited;
                break;
            case PHAuthorizationStatusAuthorized:
                _status = RNPermissionStatusAuthorized;
                break;
            default:
                _status=RNPermissionStatusNotDetermined;
        }
    } else {
        _status=RNPermissionStatusNotAvailable;
    }

    return resolve(@{@"status": @(_status)});
}

- (void)requestPhotoLibraryAddOnlyWithResolver:(void (^ _Nonnull)(id))resolve
                                      rejecter:(void (^ _Nonnull)(NSError * _Nonnull))reject {
    if (@available(iOS 14.0, *)) {
        [PHPhotoLibrary requestAuthorizationForAccessLevel:PHAccessLevelAddOnly handler:^(__unused PHAuthorizationStatus status) {
            [self checkPhotoLibraryAddOnlyWithResolver:resolve rejecter:reject];
        }];
    } else {
        return resolve(@{@"status": @(RNPermissionStatusNotAvailable)});
    }
}

/**
 * MediaLibrary
 *
 */
- (void)checkMediaLibraryWithResolver:(void (^ _Nonnull)(id))resolve
                             rejecter:(void (__unused ^ _Nonnull)(NSError * _Nonnull))reject {
    RNPermissionStatus _status= RNPermissionStatusNotDetermined;
#if TARGET_OS_SIMULATOR
    _status=RNPermissionStatusNotAvailable;
#else
    switch ([MPMediaLibrary authorizationStatus]) {
        case MPMediaLibraryAuthorizationStatusNotDetermined:
            _status=RNPermissionStatusNotDetermined;
            break;
        case MPMediaLibraryAuthorizationStatusRestricted:
            _status=RNPermissionStatusRestricted;
            break;
        case MPMediaLibraryAuthorizationStatusDenied:
            _status=RNPermissionStatusDenied;
            break;
        case MPMediaLibraryAuthorizationStatusAuthorized:
            _status=RNPermissionStatusAuthorized;
            break;
        default:
            break;
    }
#endif
    return resolve(@{@"status": @(_status)});
}

- (void)requestMediaLibraryWithResolver:(void (^ _Nonnull)(id))resolve
                               rejecter:(void (^ _Nonnull)(NSError * _Nonnull))reject {
#if TARGET_OS_SIMULATOR
    resolve(@(RNPermissionStatusNotAvailable));
#else
    [MPMediaLibrary requestAuthorization:^(__unused MPMediaLibraryAuthorizationStatus status) {
        [self checkMediaLibraryWithResolver:resolve rejecter:reject];
    }];
#endif
}

/**
 *
 * CoreLoaction
 */
- (void)checkCoreLocationWithResolver:(void (^ _Nonnull)(id))resolve
                             rejecter:(void (__unused ^ _Nonnull)(NSError * _Nonnull))reject {
    if (![CLLocationManager locationServicesEnabled]) {
        return resolve(@{@"status":@(RNPermissionStatusNotAvailable)});
    }

    switch ([CLLocationManager authorizationStatus]) {
        case kCLAuthorizationStatusNotDetermined:
            return resolve(@{@"status":@(RNPermissionStatusNotDetermined)});
        case kCLAuthorizationStatusRestricted:
            return resolve(@{@"status":@(RNPermissionStatusRestricted)});
        case kCLAuthorizationStatusDenied:
            return resolve(@{@"status":@(RNPermissionStatusDenied)});
        case kCLAuthorizationStatusAuthorizedWhenInUse:
        case kCLAuthorizationStatusAuthorizedAlways:
            return resolve(@{@"status":@(RNPermissionStatusAuthorized)});
    }
}

- (void)requestCoreLocationWithResolver:(void (^ _Nonnull)(id))resolve
                               rejecter:(void (^ _Nonnull)(NSError * _Nonnull))reject {
    if (![CLLocationManager locationServicesEnabled]) {
        return resolve(@{@"status":@(RNPermissionStatusNotAvailable)});
    }
    if ([CLLocationManager authorizationStatus] != kCLAuthorizationStatusNotDetermined) {
        return [self checkCoreLocationWithResolver:resolve rejecter:reject];
    }

    _resolve = resolve;
    _reject = reject;

    _locationManager = [CLLocationManager new];
    [_locationManager setDelegate:self];
    [_locationManager requestWhenInUseAuthorization];
}

- (void)locationManager:(CLLocationManager *)manager didChangeAuthorizationStatus:(CLAuthorizationStatus)status {
    if (status != kCLAuthorizationStatusNotDetermined) {
        [_locationManager setDelegate:nil];
        [self checkCoreLocationWithResolver:_resolve rejecter:_reject];
    }
}

/**
 * Bluetooth
 *
 */
- (void)checkBluetoothWithResolver:(void (^ _Nonnull)(id))resolve
                          rejecter:(void (__unused ^ _Nonnull)(NSError * _Nonnull))reject {
#if TARGET_OS_SIMULATOR
    return resolve(@(RNPermissionStatusNotAvailable));
#else

    if (@available(iOS 13.1, *)) {
        switch ([CBManager authorization]) {
            case CBManagerAuthorizationNotDetermined:
                return resolve(@{@"status":@(RNPermissionStatusNotDetermined)});
            case CBManagerAuthorizationRestricted:
                return resolve(@{@"status":@(RNPermissionStatusRestricted)});
            case CBManagerAuthorizationDenied:
                return resolve(@{@"status":@(RNPermissionStatusDenied)});
            case CBManagerAuthorizationAllowedAlways:
                return resolve(@{@"status":@(RNPermissionStatusAuthorized)});
        }
    } else {
        switch ([CBPeripheralManager authorizationStatus]) {
            case CBPeripheralManagerAuthorizationStatusNotDetermined:
                return resolve(@{@"status":@(RNPermissionStatusNotDetermined)});
            case CBPeripheralManagerAuthorizationStatusRestricted:
                return resolve(@{@"status":@(RNPermissionStatusRestricted)});
            case CBPeripheralManagerAuthorizationStatusDenied:
                return resolve(@{@"status":@(RNPermissionStatusDenied)});
            case CBPeripheralManagerAuthorizationStatusAuthorized:
                return resolve(@{@"status":@(RNPermissionStatusAuthorized)});
        }
    }
#endif
}

- (void)requestBluetoothWithResolver:(void (^ _Nonnull)(id))resolve
                            rejecter:(void (^ _Nonnull)(NSError * _Nonnull))reject {
#if TARGET_OS_SIMULATOR
    return resolve(@(RNPermissionStatusNotAvailable));
#else
    _resolve = resolve;
    _reject = reject;

    _manager = [[CBPeripheralManager alloc] initWithDelegate:self queue:nil options:@{
        CBPeripheralManagerOptionShowPowerAlertKey: @false,
    }];
#endif
}

- (void)peripheralManagerDidUpdateState:(nonnull CBPeripheralManager *)peripheral {
    switch (peripheral.state) {
        case CBManagerStatePoweredOff:
        case CBManagerStateResetting:
        case CBManagerStateUnsupported:
            return _resolve(@{@"status":@(RNPermissionStatusNotAvailable)});
        case CBManagerStateUnknown:
            return _resolve(@{@"status":@(RNPermissionStatusNotDetermined)});
        case CBManagerStateUnauthorized:
            return _resolve(@{@"status":@(RNPermissionStatusDenied)});
        case CBManagerStatePoweredOn:
            return [self checkBluetoothWithResolver:_resolve rejecter:_reject];
    }
}

RCT_EXPORT_MODULE()
/**
 *  openSettings
 *  打开设置页面
 */

RCT_REMAP_METHOD(openSettings,
                 openSettingsWithResolver:(RCTPromiseResolveBlock)resolve
                 rejecter:(RCTPromiseRejectBlock)reject) {
    UIApplication *sharedApplication = [UIApplication sharedApplication];
    NSURL *url = [NSURL URLWithString:UIApplicationOpenSettingsURLString];
    dispatch_async(dispatch_get_main_queue(), ^{
        [sharedApplication openURL:url options:@{} completionHandler:^(BOOL success) {
            if (success) {
                resolve(@(true));
            } else {
                reject(@"cannot_open_settings", @"Cannot open application settings", nil);
            }
        }];
    });

}

// Microphone
// Methods
RCT_EXPORT_METHOD(checkMicrophone:(RCTPromiseResolveBlock)resolve
                  reject:(RCTPromiseRejectBlock)reject) {
    dispatch_async(dispatch_get_main_queue(), ^{
        id _reject = (void(^)(NSError *)){};
        [self checkMicrophoneWithResolver:resolve rejecter:_reject];
    });
}

RCT_EXPORT_METHOD(requestMicrophone:(RCTPromiseResolveBlock)resolve
                  reject:(RCTPromiseRejectBlock)reject) {
    dispatch_async(dispatch_get_main_queue(), ^{
        id _reject = (void(^)(NSError *)){};
        [self requestMicrophoneWithResolver:resolve rejecter:_reject];
    });
}

// Calendar
// Methods
RCT_EXPORT_METHOD(checkCalendar:(RCTPromiseResolveBlock)resolve
                  reject:(RCTPromiseRejectBlock)reject) {
    dispatch_async(dispatch_get_main_queue(), ^{
        id _reject = (void(^)(NSError *)){};
        [self checkCalendarWithResolver:resolve rejecter:_reject];
    });
}

RCT_EXPORT_METHOD(requestCalendar:(RCTPromiseResolveBlock)resolve
                  reject:(RCTPromiseRejectBlock)reject) {
    dispatch_async(dispatch_get_main_queue(), ^{
        id _reject = (void(^)(NSError *)){};
        [self requestCalendarWithResolver:resolve rejecter:_reject];
    });
}
// Camera
// Methods
RCT_EXPORT_METHOD(checkCamera:(RCTPromiseResolveBlock)resolve
                  reject:(RCTPromiseRejectBlock)reject) {
    dispatch_async(dispatch_get_main_queue(), ^{
        id _reject = (void(^)(NSError *)){};
        [self checkCameraWithResolver:resolve rejecter:_reject];
    });
}

RCT_EXPORT_METHOD(requestCamera:(RCTPromiseResolveBlock)resolve
                  reject:(RCTPromiseRejectBlock)reject) {
    dispatch_async(dispatch_get_main_queue(), ^{
        id _reject = (void(^)(NSError *)){};
        [self requestCameraWithResolver:resolve rejecter:_reject];
    });
}

// PhotoLibrary
// Methods
RCT_EXPORT_METHOD(checkPhotoLibrary:(RCTPromiseResolveBlock)resolve
                  reject:(RCTPromiseRejectBlock)reject) {
    dispatch_async(dispatch_get_main_queue(), ^{
        id _reject = (void(^)(NSError *)){};
        [self checkPhotoLibraryWithResolver:resolve rejecter:_reject];
    });
}

RCT_EXPORT_METHOD(requestPhotoLibrary:(RCTPromiseResolveBlock)resolve
                  reject:(RCTPromiseRejectBlock)reject) {
    dispatch_async(dispatch_get_main_queue(), ^{
        id _reject = (void(^)(NSError *)){};
        [self requestPhotoLibraryWithResolver:resolve rejecter:_reject];
    });
}
RCT_EXPORT_METHOD(openLimitedPhotoLibraryPicker:(RCTPromiseResolveBlock)resolve
                  reject:(RCTPromiseRejectBlock)reject) {
    dispatch_async(dispatch_get_main_queue(), ^{
        [self openLimitedPhotoLibraryPickerWithResolver:resolve rejecter:reject];
    });
}
RCT_EXPORT_METHOD(checkPhotoLibraryAddOnly:(RCTPromiseResolveBlock)resolve
                  reject:(RCTPromiseRejectBlock)reject) {
    dispatch_async(dispatch_get_main_queue(), ^{
        id _reject = (void(^)(NSError *)){};
        [self checkPhotoLibraryAddOnlyWithResolver:resolve rejecter:_reject];
    });
}

RCT_EXPORT_METHOD(requestPhotoLibraryAddOnly:(RCTPromiseResolveBlock)resolve
                  reject:(RCTPromiseRejectBlock)reject) {
    dispatch_async(dispatch_get_main_queue(), ^{
        id _reject = (void(^)(NSError *)){};
        [self requestPhotoLibraryAddOnlyWithResolver:resolve rejecter:_reject];
    });
}
// MediaLibrary
// Methods
RCT_EXPORT_METHOD(checkMediaLibrary:(RCTPromiseResolveBlock)resolve
                  reject:(RCTPromiseRejectBlock)reject) {
    dispatch_async(dispatch_get_main_queue(), ^{
        id _reject = (void(^)(NSError *)){};
        [self checkMediaLibraryWithResolver:resolve rejecter:_reject];
    });
}

RCT_EXPORT_METHOD(requestMediaLibrary:(RCTPromiseResolveBlock)resolve
                  reject:(RCTPromiseRejectBlock)reject) {
    dispatch_async(dispatch_get_main_queue(), ^{
        id _reject = (void(^)(NSError *)){};
        [self requestMediaLibraryWithResolver:resolve rejecter:_reject];
    });
}
// CoreLocation
// Methods
RCT_EXPORT_METHOD(checkCoreLocation:(RCTPromiseResolveBlock)resolve
                  reject:(RCTPromiseRejectBlock)reject) {
    dispatch_async(dispatch_get_main_queue(), ^{
        id _reject = (void(^)(NSError *)){};
        [self checkCoreLocationWithResolver:resolve rejecter:_reject];
    });
}

RCT_EXPORT_METHOD(requestCoreLocation:(RCTPromiseResolveBlock)resolve
                  reject:(RCTPromiseRejectBlock)reject) {
    dispatch_async(dispatch_get_main_queue(), ^{
        id _reject = (void(^)(NSError *)){};
        [self requestCoreLocationWithResolver:resolve rejecter:_reject];
    });
}

// Bluetooth
// Methods
RCT_EXPORT_METHOD(checkBluetooth:(RCTPromiseResolveBlock)resolve
                  reject:(RCTPromiseRejectBlock)reject) {
    dispatch_async(dispatch_get_main_queue(), ^{
        id _reject = (void(^)(NSError *)){};
        [self checkBluetoothWithResolver:resolve rejecter:_reject];
    });
}

RCT_EXPORT_METHOD(requestBluetooth:(RCTPromiseResolveBlock)resolve
                  reject:(RCTPromiseRejectBlock)reject) {
    dispatch_async(dispatch_get_main_queue(), ^{
        id _reject = (void(^)(NSError *)){};
        [self requestBluetoothWithResolver:resolve rejecter:_reject];
    });
}



// Example method
// See // https://reactnative.dev/docs/native-modules-ios
RCT_REMAP_METHOD(multiply,
                 multiplyWithA:(double)a withB:(double)b
                 withResolver:(RCTPromiseResolveBlock)resolve
                 withRejecter:(RCTPromiseRejectBlock)reject)
{
    NSNumber *result = @(a * b);

    resolve(result);
}

@end
